; Switches tasks if in Multitasking mode
Tasker:
	ld a,(MultitaskEnabled)
	if(ENABLED)
		jp nz,InterruptDone
	call CheckForEntries
	if(NO_THREADS)
		jp z,InterruptDone
	ld c,0
	ld a,(CurrentThreadID)		; get the current entry ID
	cp -1				; check if it is valid
		jr z,_			; if not jump forward
	ld b,a				; store the current thread in b
	call GetNextThread		; find out the next thread
	cp b				; compare them
		jp z,InterruptDone	; if they are the same then no need to switch
	ld a,b
	call GetThreadEntry	; otherwise get the entry
	ld de,3			; stack pointer entry is at 0x0003
	add hl,de		; get the stack pointer entry in hl
	ex de,hl		; put it in de
	ld hl, 0
	add hl, sp		; hl contains the current stack position
	ex de,hl
	ld (hl),e
	inc hl
	ld (hl),d		; store the stack pointer into the stack pointer entry
_	call GetNextThread
	call GetThreadEntry
	ld (CurrentThreadID),a
	ld a,(hl)
	if(STATE_STARTED)
		jr nz,_
	push af
		ld a,STATE_RUNNING
		ld (hl),a
	pop af
	ld c,1
_	inc hl \ inc hl \ inc hl	; skip past state flag and executable address
	ld e,(hl)
	inc hl
	ld d,(hl)	; load the stack address in de
	ex de,hl	; swap de and hl
	ld sp,hl	; load the stack address into hl
	ex de,hl	; load the thread entry
	ld a,c		; check for new thread
	or a		; if not
		jp z,InterruptDone	; finish the interrupt
	dec hl \ dec hl \ dec hl	; get back to the executable address
	ld e,(hl)
	inc hl
	ld d,(hl)			; load the executable address into de
	ex de,hl			; and put it in hl
	ld a,INTERRUPT_MASK		; Enable interrupting hardware (ON and timer 2)
	out (3), a
	ei				; enable interrupts
	jp (hl)				; and jump to the program
; attempts to end the current running thread
EndThread:
	ld a,(CurrentThreadID)
	call FreeThreadMem	; Clear all RAM allocated to this thread
	call GetThreadEntry	; get the thread entry
	ld a,STATE_NONE		; change it's state to NONE
	ld (hl),a		; apply the change
	inc hl
	ld e,(hl)		; load the executable address
	inc hl
	ld d,(hl)		; into DE
	ld sp,RAMEnd-2		; change the stack pointer
	ld a,-1			; set the current thread ID
	ld (CurrentThreadID),a	; as invalid to skip a check in the thing
	call CheckForEntries	; Check for entries
	if(THREADS_PRESENT)	; if there are entries
		jp z, Tasker	; go to the next thread
	call ClearBuffer	; clear the screen
	call FastCopy
	jp Boot			; nothing is running so lets return to boot
; Creates a new task to be run
; inputs:	de = pointer to filename
; outputs:	a  = status value
NewThread:
	call SafeInterruptDI
	push af
	push de
		call LoadExecutableToRAM
		if(SUCCESS)
			jr nz,_	; failed to load
		call GetNextFreeThread
		if(OPEN_THREAD_SPACE)
			jr nz,_	; no space for new threads to go
		ld (hl),STATE_STARTED
		inc hl		; load the thread's state
		ld (hl),e
		inc hl
		ld (hl),d
		inc hl		; load the exacutable address
		ex de,hl
		add hl,bc
		ex de,hl
		ld (hl),e
		inc hl
		ld (hl),d
		inc hl		; load the stack address
	pop de
	ld (hl),e
	inc hl
	ld (hl),d		; load the pointer to the filename into the thread entry
	pop af
	jr c,$+3
	ei
	return(SUCCESS)
_	pop de
	ld b,a
	pop af
	jr c,$+3
	ei
	ld a,b
	ret
; Returns the next open thread space in hl
; outputs:	hl = pointer to open thread space
;		a  = status value
GetNextFreeThread:
	push de
	push bc
		ld hl,ThreadTable
_			ld a,(hl)
			if(STATE_NONE)
				jr z,_
			ld b,0
			ld c,ThreadSize
			add hl,bc
			push hl
				ld de,ThreadTableEnd
				or a \ sbc hl,de	; sub hl,de
				ld a,h
				or a
					jr nz,++++_
				ld a,l
				or a
					jr z,+++_	; if at the end of the table
				
			pop hl
			jr -_
_		ld a,OPEN_THREAD_SPACE
_	pop bc
	pop de
	ret
_			pop hl
			ld a,NO_THREAD_SPACE
			jr --_
_		pop hl
		jr ----_
; gets the next thread from CurrentThreadID
; outputs	a  = next thread
GetNextThread:
	push de
	push hl
	push bc
		ld a,(CurrentThreadID)	; get the current thread
_		inc a			; increment to the next thread
		ld b,MaxThreads		; if it is past the last thread
		cp b			; check
			jr nz,_		; if so jump forward
		xor a			; otherwise reset to the first pointer
		jr +++_ +1		; and exit
_		push af			; retain number
			call GetThreadEntry	; get the thread pointer
			ld a,(hl)		; load the status into a
			if(STATE_NONE)		; if no thread is present
				jr z,_		; jump forward
			jr ++_			; otherwise you have a match, safe to exit now
_		pop af			; get number back
		jr ---_			; go back to loop for the next one
_		pop af	; get number
	pop bc	; pop off all the other registers
	pop hl
	pop de
	ret	; return with a
; Checks to see if there are any thread entries in the table
; output	a  = status code
CheckForEntries:
	push hl
	push de
	push bc
		ld a,-1
_		inc a
		ld b,MaxThreads
		cp b
			jr z,++_
		push af
			call GetThreadEntry
			ld a,(hl)
			if(STATE_NONE)
				jr nz,_
		pop af
		jr -_
_		pop af
	pop hl
	pop de
	pop bc
	ld a,THREADS_PRESENT
	ret
_	pop hl
	pop de
	pop bc
	ld a,NO_THREADS
	ret
; gets the thread entry from the table
; inputs	a  = thread to get
; outputs	hl = pointer to entry
GetThreadEntry:
	push af
		add a, a
		add a, a
		add a, a ; A * 8
		ld hl, ThreadTable
		add a, l
		ld l, a
		jr nc, $+3
		inc h
	pop af
	ret
